import * as ejs from 'ejs';
import * as fs from 'fs';
import * as path from 'path';
import { promisify } from 'util';
import { GenTable, GenTableColumn } from '@/models/tool';
import genConfig from '@/config/generator';
import DateUtils from '@/utils/javaScript/date.util';
import { camelCase, upperFirst } from 'lodash';

// 文件操作Promise化
const readFileAsync = promisify(fs.readFile);
const writeFileAsync = promisify(fs.writeFile);
const mkdirAsync = promisify(fs.mkdir);
const existsAsync = promisify(fs.exists);

/**
 * 模板引擎工具类
 */
class TemplateEngine {
  /**
   * 准备模板变量上下文
   * @param genTable 业务表
   * @returns 模板上下文
   */
  public static async prepareContext(genTable: GenTable): Promise<any> {
    const { moduleName, businessName, packageName, tplCategory, functionName } = genTable;
    const pkColumn = genTable.columns?.find(col => col.isPk === '1');

    // 构造上下文对象
    const context = {
      // 表信息
      tplCategory,
      tableName: genTable.tableName,
      functionName: functionName || '【请填写功能名称】',
      ClassName: genTable.className,
      className: this.uncapitalize(genTable.className),
      moduleName: genTable.moduleName,
      BusinessName: upperFirst(camelCase(businessName)),
      businessName: businessName,
      basePackage: this.getPackagePrefix(packageName),
      packageName,
      author: genTable.functionAuthor,
      datetime: DateUtils.getDate(),
      pkColumn,
      importList: this.getImportList(genTable),
      permissionPrefix: this.getPermissionPrefix(moduleName, businessName),
      columns: genTable.columns || [],
      table: genTable,
      dicts: this.getDicts(genTable)
    };

    // 为树形表和主子表添加额外上下文
    if (tplCategory === 'tree') {
      this.setTreeContext(context, genTable);
    }
    if (tplCategory === 'sub') {
      this.setSubContext(context, genTable);
    }

    return context;
  }

  /**
   * 为树形表添加上下文
   * @param context 上下文对象
   * @param genTable 业务表
   */
  private static setTreeContext(context: any, genTable: GenTable): void {
    const options = genTable.options ? JSON.parse(genTable.options) : {};
    context.treeCode = options.treeCode || '';
    context.treeParentCode = options.treeParentCode || '';
    context.treeName = options.treeName || '';
    context.expandColumn = this.getExpandColumn(genTable);
    context.tree_parent_code = options.treeParentCode || '';
    context.tree_name = options.treeName || '';
  }

  /**
   * 为主子表添加上下文
   * @param context 上下文对象
   * @param genTable 业务表
   */
  private static setSubContext(context: any, genTable: GenTable): void {
    const subTable = genTable.subTable;
    if (!subTable) return;

    const subTableName = genTable.subTableName || '';
    const subTableFkName = genTable.subTableFkName || '';
    const subClassName = subTable.className || '';
    const subTableFkClassName = upperFirst(camelCase(subTableFkName));

    context.subTable = subTable;
    context.subTableName = subTableName;
    context.subTableFkName = subTableFkName;
    context.subTableFkClassName = subTableFkClassName;
    context.subTableFkclassName = this.uncapitalize(subTableFkClassName);
    context.subClassName = subClassName;
    context.subclassName = this.uncapitalize(subClassName);
    context.subImportList = this.getImportList(subTable);
  }

  /**
   * 获取需要导入的包列表
   * @param genTable 业务表
   * @returns 导入包列表
   */
  private static getImportList(genTable: GenTable): Set<string> {
    const columns = genTable.columns || [];
    const importSet = new Set<string>();
    
    // 检查每列类型，添加需要的导入
    columns.forEach(column => {
      if (column.javaType === 'Date') {
        importSet.add('java.util.Date');
      } else if (column.javaType === 'BigDecimal') {
        importSet.add('java.math.BigDecimal');
      }
    });
    
    return importSet;
  }

  /**
   * 获取字典列表
   * @param genTable 业务表
   * @returns 字典列表
   */
  private static getDicts(genTable: GenTable): string[] {
    const dicts: Set<string> = new Set();
    const columns = genTable.columns || [];
    
    // 收集所有有字典的列
    columns.forEach(column => {
      if (column.dictType && column.dictType.trim() !== '') {
        dicts.add(column.dictType);
      }
    });
    
    return Array.from(dicts);
  }

  /**
   * 获取权限前缀
   * @param moduleName 模块名
   * @param businessName 业务名
   * @returns 权限前缀
   */
  private static getPermissionPrefix(moduleName: string, businessName: string): string {
    return `${moduleName}:${businessName}`;
  }

  /**
   * 获取包前缀
   * @param packageName 包名
   * @returns 包前缀
   */
  private static getPackagePrefix(packageName: string): string {
    const lastIndex = packageName.lastIndexOf('.');
    return lastIndex > -1 ? packageName.substring(0, lastIndex) : packageName;
  }

  /**
   * 获取树形表的展开列
   * @param genTable 业务表
   * @returns 展开列索引
   */
  private static getExpandColumn(genTable: GenTable): number {
    const columns = genTable.columns || [];
    // 从第一个非主键列开始查找
    for (let i = 0; i < columns.length; i++) {
      if (columns[i].isPk !== '1') {
        return i;
      }
    }
    return 0;
  }

  /**
   * 首字母小写
   * @param str 字符串
   * @returns 首字母小写的字符串
   */
  private static uncapitalize(str: string): string {
    if (!str || str.length === 0) return str;
    return str.charAt(0).toLowerCase() + str.slice(1);
  }

  /**
   * 获取模板列表
   * @param tplCategory 模板类型
   * @param tplWebType 前端模板类型
   * @returns 模板文件列表
   */
  public static getTemplateList(tplCategory: string, tplWebType: string): string[] {
    const useWebType = tplWebType === 'element-plus' ? 'vue/v3' : 'vue';
    const templates: string[] = [];
    
    // 添加基础模板
    templates.push('ts/model.ts.ejs');
    templates.push('ts/service.ts.ejs');
    templates.push('ts/controller.ts.ejs');
    templates.push('ts/routes.ts.ejs');
    templates.push('sql/sql.ejs');
    templates.push('js/api.js.ejs');
    
    // 根据表类型添加不同的前端模板
    if (tplCategory === 'crud') {
      templates.push(`${useWebType}/index.vue.ejs`);
    } else if (tplCategory === 'tree') {
      templates.push(`${useWebType}/index-tree.vue.ejs`);
    } else if (tplCategory === 'sub') {
      templates.push(`${useWebType}/index.vue.ejs`);
      templates.push('ts/sub-model.ts.ejs');
    }
    
    return templates;
  }

  /**
   * 获取生成文件名
   * @param template 模板路径
   * @param genTable 业务表
   * @returns 生成文件名
   */
  public static getFileName(template: string, genTable: GenTable): string {
    // 文件名称
    let fileName = '';
    // 包路径
    const packageName = genTable.packageName;
    // 模块名
    const moduleName = genTable.moduleName;
    // 大写类名
    const className = genTable.className;
    // 业务名称
    const businessName = genTable.businessName;
    
    const javaPath = `src/main/java/${packageName.replace(/\./g, '/')}`;
    const tsPath = 'src';
    const vuePath = 'vue';
    
    if (template.includes('model.ts.ejs')) {
      fileName = `${tsPath}/models/${moduleName}/${businessName}.model.ts`;
    } else if (template.includes('sub-model.ts.ejs') && genTable.tplCategory === 'sub') {
      fileName = `${tsPath}/models/${moduleName}/${genTable.subTable?.businessName || 'sub'}.model.ts`;
    } else if (template.includes('service.ts.ejs')) {
      fileName = `${tsPath}/services/${moduleName}/${businessName}.service.ts`;
    } else if (template.includes('controller.ts.ejs')) {
      fileName = `${tsPath}/controllers/${moduleName}/${businessName}.controller.ts`;
    } else if (template.includes('routes.ts.ejs')) {
      fileName = `${tsPath}/routes/${moduleName}/${businessName}.routes.ts`;
    } else if (template.includes('sql.ejs')) {
      fileName = `${businessName}Menu.sql`;
    } else if (template.includes('api.js.ejs')) {
      fileName = `${vuePath}/api/${moduleName}/${businessName}.js`;
    } else if (template.includes('index.vue.ejs')) {
      fileName = `${vuePath}/views/${moduleName}/${businessName}/index.vue`;
    } else if (template.includes('index-tree.vue.ejs')) {
      fileName = `${vuePath}/views/${moduleName}/${businessName}/index.vue`;
    }
    
    return fileName;
  }

  /**
   * 渲染模板
   * @param templateFile 模板文件
   * @param context 模板上下文
   * @returns 渲染后的内容
   */
  public static async renderTemplate(templateFile: string, context: any): Promise<string> {
    try {
      const templatePath = path.join(process.cwd(), genConfig.templatePath, templateFile);
      const template = await readFileAsync(templatePath, 'utf-8');
      return ejs.render(template, context);
    } catch (err) {
      console.error(`渲染模板 ${templateFile} 失败`, err);
      throw err;
    }
  }

  /**
   * 创建代码文件
   * @param fileName 文件名
   * @param content 文件内容
   * @param outputDir 输出目录
   * @returns 是否成功
   */
  public static async createCodeFile(fileName: string, content: string, outputDir: string): Promise<boolean> {
    try {
      // 目标文件完整路径
      const targetFile = path.join(outputDir, fileName);
      
      // 创建目录（如果不存在）
      const dirPath = path.dirname(targetFile);
      if (!await existsAsync(dirPath)) {
        await mkdirAsync(dirPath, { recursive: true });
      }
      
      // 写入文件
      await writeFileAsync(targetFile, content);
      return true;
    } catch (err) {
      console.error(`写入文件 ${fileName} 失败`, err);
      return false;
    }
  }
}

export default TemplateEngine; 